package com.zmn.plat.anole.config.interceptor;


import com.zmn.plat.anole.interfaces.IEncryptDecrypt;
import com.zmn.plat.common.util.encrypt.annotation.EncryptDecryptClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;

/**
 * 描述: MyBatis 参数拦截器
 *
 * @author jiafei
 * @version v1.0
 * @since 2021/8/25 17:36
 */
@Intercepts({
		@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@Component
@Slf4j
public class ParamInterceptor implements Interceptor {

	@Resource
	private IEncryptDecrypt encryptDecrypt;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {

		//拦截 ParameterHandler 的 setParameters 方法 动态设置参数
		if (invocation.getTarget() instanceof ParameterHandler) {
			ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();

			// 反射获取 参数对像
			Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
			Field mappedStatement = parameterHandler.getClass().getDeclaredField("mappedStatement");
			parameterField.setAccessible(true);
			mappedStatement.setAccessible(true);
			Object parameterObject = parameterField.get(parameterHandler);
			MappedStatement ms = (MappedStatement) mappedStatement.get(parameterHandler);
			if (!isSelect(ms.getSqlCommandType()) && Objects.nonNull(parameterObject)) {
				Class<?> parameterObjectClass = parameterObject.getClass();
				EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass,
																						 EncryptDecryptClass.class);
				if (Objects.nonNull(encryptDecryptClass)) {
					encryptDecrypt.encrypt(parameterObject);
				}
			}
		}
		return invocation.proceed();
	}

	public boolean needToDecrypt(Object object) {
		if (Objects.isNull(object)) {
			return false;
		}

		Class<?> objectClass = object.getClass();
		EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass,
																				 EncryptDecryptClass.class);
		return Objects.nonNull(encryptDecryptClass);
	}

	private boolean isSelect(SqlCommandType sqlCommandType) {

		switch (sqlCommandType) {
			case SELECT:
				return true;
			case INSERT:
			case UPDATE:
			case DELETE:
			default:
				return false;
		}
	}

	@Override
	public Object plugin(Object o) {

		return Plugin.wrap(o, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

}

